package com.third.customer.service.impl;

import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Sets;
import com.third.customer.model.dao.AreaDao;
import com.third.customer.model.dao.CustomerDao;
import com.third.customer.model.dao.CustomerGroupDao;
import com.third.customer.model.dao.CustomerHistoryDao;
import com.third.customer.model.dao.param.CustomerHistoryParam;
import com.third.customer.model.dao.param.CustomerParam;
import com.third.customer.model.mapper.CustomerMapper;
import com.third.customer.model.tables.Area;
import com.third.customer.model.tables.Customer;
import com.third.customer.model.tables.CustomerGroup;
import com.third.customer.model.tables.CustomerHistory;
import com.third.customer.service.AreaService;
import com.third.customer.service.CustomerGroupService;
import com.third.customer.service.CustomerService;
import com.third.customer.service.data.CustomerReport;
import com.third.customer.service.data.CustomerReq;
import com.third.customer.service.data.CustomerSwitchReq;
import com.third.customer.service.exception.ThirdBadRequestException;
import com.third.customer.service.exception.ThirdNotFoundException;
import com.third.customer.service.utils.ExcelUtils;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;

/**
 * Created by hehuaichun on 2020/5/23.
 */
@Slf4j
@Service
public class CustomerServiceimpl extends ServiceImpl<CustomerMapper, Customer> implements CustomerService {

    @Autowired
    private CustomerDao customerDao;

    @Autowired
    private CustomerHistoryDao customerHistoryDao;

    @Autowired
    private CustomerGroupDao customerGroupDao;

    @Autowired
    private AreaDao areaDao;

    @Autowired
    private AreaService areaService;

    @Autowired
    private CustomerGroupService customerGroupService;


    @Override
    public Page<Customer> list(CustomerParam param) {
        return customerDao.query(param);
    }

    @Override
    public Customer getById(Long companyId, Long customerId) {
        return customerDao.getById(companyId, customerId)
                .orElseThrow(() -> new ThirdNotFoundException("客户不存在!"));
    }

    @Override
    public Customer create(Long companyId, CustomerReq req) {
        check(companyId, req, true);
        Customer customer = buildCustomer(null, companyId, req);
        customerDao.insert(customer);
        //todo 记录操作
        return customer;
    }

    @Override
    public Customer modify(Long companyId, Long customerId, CustomerReq req) {
        Customer old = getById(companyId, customerId);
        boolean needCheckPhone = !StringUtils.isEmpty(req.getPhone()) && Objects.equals(req.getPhone(), old.getPhone());
        check(companyId, req, needCheckPhone);
        Customer customer = buildCustomer(customerId, companyId, req);
        customerDao.update(customer);
        //todo 记录操作
        return customer;
    }

    @Override
    public boolean delete(Long companyId, Long customerId) {
        getById(companyId, customerId);
        boolean success = customerDao.delete(customerId);
        //todo 记录
        return success;
    }

    @Override
    public boolean switchGroup(Long companyId, Long customerId, CustomerSwitchReq req) {
        getById(companyId, customerId);
        Customer customer = new Customer();
        customer.setId(customerId);
        customer.setGroupId(req.getGroupId());
        customer.setRemark(req.getRemark());
        boolean success = customerDao.update(customer);
        //todo 记录操作
        return success;
    }

    @Override
    public boolean analysis(Long companyId, MultipartFile file, boolean forceUpdate) throws IOException {
        Map<String, CustomerReport> phone2Report = ExcelUtils.read(file.getInputStream(), file.getOriginalFilename(), CustomerReport.class)
                .collect(Collectors.toMap(CustomerReport::getPhone, Function.identity()));
        Map<String, Customer> phone2Customer = customerDao.getByCompanyId(companyId).stream()
                .collect(Collectors.toMap(Customer::getPhone, Function.identity()));
        Set<String> noExists = Sets.difference(phone2Report.keySet(), phone2Customer.keySet());

        //查询消费组名称对应的消费组主键id  & 查询地区对应的地区id
        Map<String, Long> groupName2GroupId = customerGroupDao.getByCompanyId(companyId)
                .stream().collect(Collectors.toMap(CustomerGroup::getName, CustomerGroup::getId));
        Map<AreaKey, Long> areaKey2AreaId = areaDao.getByCompanyId(companyId)
                .stream().collect(Collectors.toMap(area -> new AreaKey(area.getCounty(), area.getCommunity()), Area::getId));

        List<Customer> news;
        if (forceUpdate) {
            news = phone2Report.entrySet().stream().map(entry -> {
                long groupId = getGroupIdOrThrow(groupName2GroupId, entry.getValue().getGroupName());
                long areaId = getAreaIdOrThrow(areaKey2AreaId, new AreaKey(entry.getValue().getCounty(), entry.getValue().getCommunity()));
                if (phone2Customer.keySet().contains(entry.getKey())) {
                    return buildCustomer(phone2Customer.get(entry.getKey()).getId(), companyId, entry.getValue(), groupId, areaId);
                }
                return buildCustomer(null, companyId, entry.getValue(), groupId, areaId);
            }).collect(Collectors.toList());
        } else {
            news = noExists.stream().map(phone -> {
                long groupId = getGroupIdOrThrow(groupName2GroupId, phone2Report.get(phone).getGroupName());
                long areaId = getAreaIdOrThrow(areaKey2AreaId, new AreaKey(phone2Report.get(phone).getCounty(), phone2Report.get(phone).getCommunity()));
                return buildCustomer(null, companyId, phone2Report.get(phone), groupId, areaId);
            }).collect(Collectors.toList());
        }
        boolean success = saveOrUpdateBatch(news);
        //todo 记录操作
        return success;
    }

    private Long getGroupIdOrThrow(Map<String, Long> groupName2GroupId, String groupName) {
        if (groupName2GroupId.containsKey(groupName)) {
            return groupName2GroupId.get(groupName);
        }
        throw new ThirdNotFoundException(String.format("有未知客户分组:[{}],请先创建", groupName));
    }

    private Long getAreaIdOrThrow(Map<AreaKey, Long> areaKey2AreaId, AreaKey areaKey) {
        if (areaKey2AreaId.containsKey(areaKey)) {
            return areaKey2AreaId.get(areaKey);
        }
        throw new ThirdNotFoundException(String.format("有未知地区分组:[{}],请先创建", areaKey.toString()));
    }

    @Override
    public List<CustomerReport> getReports(CustomerParam param) {
        List<Customer> customers = list(param).getRecords();
        List<Long> groupIds = customers.stream().map(Customer::getGroupId).distinct().collect(Collectors.toList());
        List<Long> areaIds = customers.stream().map(Customer::getAreaId).distinct().collect(Collectors.toList());
        Map<Long, String> groupId2Name = customerGroupDao.getByIds(param.getCompanyId(), groupIds)
                .stream().collect(Collectors.toMap(CustomerGroup::getId, CustomerGroup::getName));
        Map<Long, Area> areaMap = areaDao.getByIds(param.getCompanyId(), areaIds)
                .stream().collect(Collectors.toMap(Area::getId, Function.identity()));
        return customers.stream().map(customer -> CustomerReport.convert(customer,
                groupId2Name.getOrDefault(customer.getGroupId(), ""),
                areaMap.getOrDefault(customer.getAreaId(), new Area()))).collect(Collectors.toList());
    }

    @Override
    public Page<CustomerHistory> histories(CustomerHistoryParam param) {
        return customerHistoryDao.query(param);
    }


    private void check(Long companyId, CustomerReq req, boolean needCheckPhone) {
        if (req.getAreaId() != null) {
            areaService.getById(companyId, req.getAreaId());
        }
        if (req.getGroupId() != null) {
            customerGroupService.getById(companyId, req.getGroupId());
        }
        if (needCheckPhone) {
            Optional<Customer> optional = customerDao.getByPhone(companyId, req.getPhone());
            if (optional.isPresent()) {
                throw new ThirdBadRequestException("客户已存在!");
            }
        }
    }

    private Customer buildCustomer(Long id, Long companyId, CustomerReq req) {
        Customer customer = new Customer();
        customer.setId(id);
        customer.setCompanyId(companyId);
        customer.setAddress(req.getAddress());
        customer.setAreaId(req.getAreaId());
        customer.setGroupId(req.getGroupId());
        customer.setName(req.getName());
        customer.setPhone(req.getPhone());
        customer.setRemark(req.getRemark());
        return customer;
    }

    private Customer buildCustomer(Long id, Long companyId, CustomerReport report,
                                   Long groupId, Long areaId) {
        Customer customer = new Customer();
        customer.setId(id);
        customer.setCompanyId(companyId);
        customer.setGroupId(groupId);
        customer.setAreaId(areaId);
        customer.setPhone(report.getPhone());
        customer.setAddress(report.getAddress());
        customer.setName(report.getName());
        customer.setRemark(report.getRemark());
        return customer;
    }

    @Data
    private class AreaKey {
        private final String county;
        private final String community;
    }
}
